use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use thiserror::Error;
use fatfs::Error as FatfsError;

#[derive(Error, Debug)]
pub enum FatFsError {
    #[error("File not found: {0}")]
    FileNotFound(String),
    
    #[error("Directory not found: {0}")]
    DirectoryNotFound(String),
    
    #[error("Not a directory")]
    NotADirectory,
    
    #[error("Not a file")]
    NotAFile,
    
    #[error("Permission denied")]
    PermissionDenied,
    
    #[error("Read-only filesystem")]
    ReadOnlyFilesystem,
    
    #[error("I/O error: {0}")]
    IoError(#[from] io::Error),
    
    #[error("Path not found: {0}")]
    PathNotFound(PathBuf),
    
    #[error("Invalid path: {0}")]
    InvalidPath(String),
    
    #[error("File already exists: {0}")]
    FileExists(String),
    
    #[error("Directory not empty: {0}")]
    DirectoryNotEmpty(String),
    
    #[error("End of file")]
    EndOfFile,
    
    #[error("FATFS error: {0}")]
    FatFs(#[from] FatfsError),
    
    #[error("Unsupported operation")]
    UnsupportedOperation,
    
    #[error("Bad file descriptor")]
    BadFileDescriptor,
    
    #[error("Out of memory")]
    OutOfMemory,
    
    #[error("Too many open files")]
    TooManyOpenFiles,
    
    #[error("Operation not permitted")]
    OperationNotPermitted,
    
    #[error("Invalid argument: {0}")]
    InvalidArgument(String),
}

impl FatFsError {
    pub fn file_not_found<P: AsRef<Path>>(path: P) -> Self {
        Self::FileNotFound(path.as_ref().display().to_string())
    }
    
    pub fn directory_not_found<P: AsRef<Path>>(path: P) -> Self {
        Self::DirectoryNotFound(path.as_ref().display().to_string())
    }
    
    pub fn path_not_found<P: AsRef<Path>>(path: P) -> Self {
        Self::PathNotFound(path.as_ref().to_path_buf())
    }
    
    pub fn file_exists<P: AsRef<Path>>(path: P) -> Self {
        Self::FileExists(path.as_ref().display().to_string())
    }
    
    pub fn directory_not_empty<P: AsRef<Path>>(path: P) -> Self {
        Self::DirectoryNotEmpty(path.as_ref().display().to_string())
    }
    
    pub fn invalid_path<S: Into<String>>(path: S) -> Self {
        Self::InvalidPath(path.into())
    }
    
    pub fn invalid_argument<S: Into<String>>(arg: S) -> Self {
        Self::InvalidArgument(arg.into())
    }
}

impl From<FatFsError> for i32 {
    fn from(error: FatFsError) -> Self {
        match error {
            FatFsError::FileNotFound(_) => fuse3_sys::ENOENT,
            FatFsError::DirectoryNotFound(_) => fuse3_sys::ENOENT,
            FatFsError::NotADirectory => fuse3_sys::ENOTDIR,
            FatFsError::NotAFile => fuse3_sys::EISDIR,
            FatFsError::PermissionDenied => fuse3_sys::EACCES,
            FatFsError::ReadOnlyFilesystem => fuse3_sys::EROFS,
            FatFsError::IoError(e) => e.raw_os_error().unwrap_or(fuse3_sys::EIO),
            FatFsError::PathNotFound(_) => fuse3_sys::ENOENT,
            FatFsError::InvalidPath(_) => fuse3_sys::ENOENT,
            FatFsError::FileExists(_) => fuse3_sys::EEXIST,
            FatFsError::DirectoryNotEmpty(_) => fuse3_sys::ENOTEMPTY,
            FatFsError::EndOfFile => fuse3_sys::EOF,
            FatFsError::FatFs(_) => fuse3_sys::EIO,
            FatFsError::UnsupportedOperation => fuse3_sys::ENOSYS,
            FatFsError::BadFileDescriptor => fuse3_sys::EBADF,
            FatFsError::OutOfMemory => fuse3_sys::ENOMEM,
            FatFsError::TooManyOpenFiles => fuse3_sys::EMFILE,
            FatFsError::OperationNotPermitted => fuse3_sys::EPERM,
            FatFsError::InvalidArgument(_) => fuse3_sys::EINVAL,
        }
    }
}

// Helper macro for error handling
#[macro_export]
macro_rules! fatfs_result {
    ($expr:expr) => {
        $expr.map_err(|e| {
            log::debug!("Error: {}", e);
            e.into()
        })
    };
}

// Helper macro for logging errors
#[macro_export]
macro_rules! log_error {
    ($expr:expr, $msg:expr) => {
        match $expr {
            Ok(v) => Ok(v),
            Err(e) => {
                log::error!("{}: {}", $msg, e);
                Err(e)
            }
        }
    };
}

// Helper macro for logging warnings
#[macro_export]
macro_rules! log_warning {
    ($expr:expr, $msg:expr) => {
        match $expr {
            Ok(v) => Ok(v),
            Err(e) => {
                log::warn!("{}: {}", $msg, e);
                Err(e)
            }
        }
    };
}